package driver;

import gene.ChinkleException;
import chromo.Chromosome;
import chromo.Population;


public class Algorithm {

	/* GA parameters */
	private static final double uniformRate = 0.05;
    private static final double mutationRate = 0.5;
    private static final int tournamentSize = 2;
    private static final boolean elitism = true;
    
    //Takes a population, does crossover, mutation, and returns the new population
    public static Population evolvePopulation(Population pop) throws ChinkleException {
        Population newPopulation = new Population(pop.size());
        // Keep our best individual
        if (elitism) {
            newPopulation.setChromosome(0, pop.getFittest());
        }
     // Crossover population
        int elitismOffset;
        if (elitism) {
            elitismOffset = 1;
        } else {
            elitismOffset = 0;
        }
        // Loop over the population size and create new individuals with
        // crossover
        for (int i = elitismOffset; i < pop.size(); i++) {
        	Chromosome chromo1 = tourney(pop);
        	Chromosome chromo2 = tourney(pop);
        	Chromosome newChromo = crossover(chromo1, chromo2);
        	newPopulation.setChromosome(i, newChromo);
        }
        for (int i = elitismOffset+1; i < newPopulation.size(); i++) {
          mutate(newPopulation.getChromosome(i));
        }
        
        return newPopulation;
    }
    //Takes two chromosomes and returns one new one.
	private static Chromosome crossover(Chromosome chromo1, Chromosome chromo2) {
		Chromosome newSol = new Chromosome(chromo1);
		
		// Loop through genes
		for (int i = 0; i < Chromosome.size; i++) {
			for (int j = 0; j < Chromosome.size; j++) {
				// Crossover
				if (Math.random() > uniformRate) 
					newSol.setGene(chromo2.getGene(i, j), i, j);
			}
		}
		return newSol;
	}
	//Uses tournament selection to find the most fit chromosome in a population
	private static Chromosome tourney(Population pop) {
		Population tournament = new Population(tournamentSize);
		for (int i = 0; i < tournamentSize; i++) {
            int randomId = (int) (Math.random() * pop.size());
            tournament.setChromosome(i, pop.getChromosome(randomId));
        }
		return tournament.getFittest();
	}
	//Will possibly mutate a certain gene and change its gates
	private static void mutate(Chromosome chromo) throws ChinkleException{
		for (int i = 0; i < Chromosome.size; i++){
			for (int j = 0; j < Chromosome.size; j++){
				if(Math.random() <= mutationRate)
					chromo.mutate(i, j);
			}
		}
	}
}
